  #
# Copyright 2013 - Tom Alessi
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""This module contains all of the views for the Berper main
   application relating to administrative functions.  All views
   should require authentication

"""


from crontab import CronTab
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.db import IntegrityError
from berper.main.models import Config
from berper.main.models import Scan
from berper.main.models import Scan_Enabled
from berper.main.models import Scan_Method
from berper.main.models import Scan_Runner
from berper.main.models import Scan_Type
from berper.main.models import Schedule
from berper.main.models import Site
from berper.main.models import Port
from berper.main.models import TScan_List
from berper.main.models import URL
from berper.main.models import Seed_Url
from berper.main.forms import AddTScanForm
from berper.main.forms import ModifyTScanForm
from berper.main.forms import AddSScanForm
from berper.main.forms import ConfigForm
from berper.main.forms import DeleteScanForm
from berper.main.forms import ModifySScanForm
from berper.main.forms import SchedulerForm
from berper.main.forms import URLListForm


def config(request):
    """Configuration Parameters

    """

    if request.method == 'POST':

        # Check the form elements
        # We'll only update values if the user indicates they want it by checking the box
        form = ConfigForm(request.POST)

        if form.is_valid():
            
            # Obtain the cleaned data and put into a lookup table
            params = {}
            params['scan_threads'] = form.cleaned_data['scan_threads']
            params['java'] = form.cleaned_data['java']
            params['burpjar'] = form.cleaned_data['burpjar']
            params['mysql_java'] = form.cleaned_data['mysql_java']
            params['burp_memory'] = form.cleaned_data['burp_memory']
            params['burp_reports'] = form.cleaned_data['burp_reports']
             
            filter = form.cleaned_data['filter'] 
                      
            # Update the data
            for param in params:
                if 'update_%s' % param in request.POST:
                    Config.objects.filter(config_name=param).update(config_value=params[param])

            # Redirect back
            return HttpResponseRedirect('/adm/config?filter=%s' % filter)
        else:
            print 'Invalid form: ConfigForm: %s' % form.errors

    # Not a POST so create a blank form and return all the existing configs
    else:
        form = ConfigForm()

    # See if we are filtering results
    # On failed POSTS, we also need this
    if 'filter' in request.POST:
        filter = request.POST['filter'] 
    elif 'filter' in request.GET:
        filter = request.GET['filter'] 
    else:
        filter = 'all'  
    
    # Obtain all of the config values
    if filter == 'all':
        configs = Config.objects.values('config_name',
                                        'friendly_name',
                                        'config_value',
                                        'description',
                                        'display')
    else:
        configs = Config.objects.filter(category=filter).values('config_name',
                                                                'friendly_name',
                                                                'config_value',
                                                                'description',
                                                                'display')

    # Print the page
    return render_to_response(
       'adm/config.html',
       {
          'title':'Berper | Configuration Manager',
          'form':form,
          'configs':configs,
          'filter':filter
       },
       context_instance=RequestContext(request)
    )


def scheduler(request):
    """Scheduler

    """

    if request.method == 'POST':

        # Check the form elements
        form = SchedulerForm(request.POST)

        if form.is_valid():

            enabled = form.cleaned_data['enabled']

            # Access the crontab
            cron = CronTab()
          
            # Remove everything from the crontab - Berper needs complete control to ensure there
            # are no errors.  If the job is enabled, we'll add it back.
            cron.remove_all('')
            
            if enabled:

                # Schedule the job
                job = cron.new(command='export PYTHONPATH=/opt/berper && /opt/berper/jobs/scan.py',
                               comment='Berper Scan Runner Job - do not modify manually!')

                # Run the job every hour
                job.hour.every(1)

                # Run the job every minute
                job.minute.every(1)

                # Write out the cron job
                cron.write()

                # Enable in the database
                Scan_Runner.objects.filter(name='enabled').update(value=True)

            else:

                # Write out the cron job
                cron.write()

                # Disable in the database
                Scan_Runner.objects.filter(name='enabled').update(value=False)
            
            # Redirect back
            return HttpResponseRedirect('/adm/scheduler')
        else:
            print 'Invalid form: SchedulerForm: %s' % form.errors

    # Not a POST so create a blank form and return all the existing configs
    else:
        form = SchedulerForm()

    # See if the scan runner is enabled or disabled
    enabled = Scan_Runner.objects.filter(name='enabled').values('value')[0]['value']

    # Print the page
    return render_to_response(
       'adm/scheduler.html',
       {
          'title':'Berper | Scheduler',
          'form':form,
          'enabled':enabled
       },
       context_instance=RequestContext(request)
    )


def sscans(request):
    """Spidered Scans view
    
    """

    # If this is a POST, then validate the form and save the data
    if request.method == 'POST':
       
        # Check the form elements
        form = AddSScanForm(request.POST)

        if form.is_valid():
            # Obtain the cleaned data
            name = form.cleaned_data['name']
            site = form.cleaned_data['site']
            hport = form.cleaned_data['hport']
            sport = form.cleaned_data['sport']
            time = form.cleaned_data['time']
            monday = form.cleaned_data['monday']
            tuesday = form.cleaned_data['tuesday']
            wednesday = form.cleaned_data['wednesday']
            thursday = form.cleaned_data['thursday']
            friday = form.cleaned_data['friday']
            saturday = form.cleaned_data['saturday']
            sunday = form.cleaned_data['sunday']
            method = form.cleaned_data['method']
            seed = form.cleaned_data['seed']
            enabled = form.cleaned_data['enabled']

            # If either hport or sport are blank, set to None
            if not hport:
                hport = None

            if not sport:
                sport = None
                
            # Add the scan name and obtain the name id
            Scan(name=name).save()
            name_id = Scan.objects.filter(name=name).values('id')[0]['id']

            # Add the site
            Site(name_id=name_id,site=site).save()

            # Whether enabled or not
            Scan_Enabled(name_id=name_id,enabled=enabled).save()
            # Add the ports

            Port(name_id=name_id,hport=hport,sport=sport).save()

            # Add the schedule
            Schedule(
                name_id=name_id,
                time=time,
                monday=monday,
                tuesday=tuesday,
                wednesday=wednesday,
                thursday=thursday,
                friday=friday,
                saturday=saturday,
                sunday=sunday
                ).save()
            
            # Add the scan type
            Scan_Type(name_id=name_id,targeted=False).save()

            # Add the scan method
            Scan_Method(name_id=name_id,active=method).save()

            # Add the seed url
            Seed_Url(name_id=name_id,path=seed).save()

            # Successful form submit so redirect 
            return HttpResponseRedirect('/adm/sscans')

        # Invalid form
        else:
            print 'Invalid form: AddSScanForm: %s' % form.errors

    
    # Its a GET or a failed POST
    else:
        # Create a blank add_tscan form
        form = AddSScanForm()

    # Obtain the spidered scans
    sscans = Scan.objects.filter(scan_type__targeted=False).values(
                                                            'id',
                                                            'name',
                                                            'site__site',
                                                            'scan_enabled__enabled',
                                                            'seed_url__path',
                                                            'scan_method__active',
                                                            'port__hport',
                                                            'port__sport',
                                                            'schedule__time',
                                                            'schedule__monday',
                                                            'schedule__tuesday',
                                                            'schedule__wednesday',
                                                            'schedule__thursday',
                                                            'schedule__friday',
                                                            'schedule__saturday',
                                                            'schedule__sunday'
                                                            )

    # Print the page
    return render_to_response(
       'adm/sscans.html',
       {
          'title':'Berper | Spidered Scan Management',
          'form':form,
          'sscans':sscans
       },
       context_instance=RequestContext(request)
    )


def sscan_modify(request):
    """Spidered Scan Modify view
    
    """

    # If this is a POST, then validate the form and save the data
    if request.method == 'POST':
       
        # Check the form elements
        form = ModifySScanForm(request.POST)

        if form.is_valid():
            # Obtain the cleaned data
            id = form.cleaned_data['id']
            hport = form.cleaned_data['hport']
            sport = form.cleaned_data['sport']
            time = form.cleaned_data['time']
            monday = form.cleaned_data['monday']
            tuesday = form.cleaned_data['tuesday']
            wednesday = form.cleaned_data['wednesday']
            thursday = form.cleaned_data['thursday']
            friday = form.cleaned_data['friday']
            saturday = form.cleaned_data['saturday']
            sunday = form.cleaned_data['sunday']
            method = form.cleaned_data['method']
            seed = form.cleaned_data['seed']
            enabled = form.cleaned_data['enabled']
  
            # Modify the state of the scan
            Scan_Enabled.objects.filter(name_id=id).update(enabled=enabled)

            # If either hport or sport are blank, set to None
            if not hport:
                hport = None

            if not sport:
                sport = None
                
            # Add the ports
            Port.objects.filter(name_id=id).update(hport=hport,sport=sport)

            # Add the schedule
            Schedule.objects.filter(name_id=id).update(
                                                        time=time,
                                                        monday=monday,
                                                        tuesday=tuesday,
                                                        wednesday=wednesday,
                                                        thursday=thursday,
                                                        friday=friday,
                                                        saturday=saturday,
                                                        sunday=sunday
                                                        )
            
            # Add the scan type
            Scan_Type.objects.filter(name_id=id).update(targeted=False)

            # Add the scan method
            Scan_Method.objects.filter(name_id=id).update(active=method)

            # Add the seed url
            Seed_Url.objects.filter(name_id=id).update(path=seed)

            # Successful form submit so redirect 
            return HttpResponseRedirect('/adm/sscans')

        # Invalid form
        else:
            print 'Invalid form: ModifySScanForm: %s' % form.errors

    
    # Its a GET or a failed POST
    else:
        # Create a blank add_tscan form
        form = ModifySScanForm()

    # We need the ID
    id = None
    if request.method == 'POST':
        if 'id' in request.POST:
            id = request.POST['id']
    else: 
        if 'id' in request.GET:
            id = request.GET['id']
    if id is None:
        return HttpResponseRedirect('/adm/sscans')

    # Obtain the specific spidered scan details
    details = Scan.objects.filter(id=id,scan_type__targeted=False).values(
                                                            'id',
                                                            'name',
                                                            'site__site',
                                                            'scan_enabled__enabled',
                                                            'seed_url__path',
                                                            'scan_method__active',
                                                            'port__hport',
                                                            'port__sport',
                                                            'schedule__time',
                                                            'schedule__monday',
                                                            'schedule__tuesday',
                                                            'schedule__wednesday',
                                                            'schedule__thursday',
                                                            'schedule__friday',
                                                            'schedule__saturday',
                                                            'schedule__sunday'
                                                            )

    # Print the page
    return render_to_response(
       'adm/sscan_modify.html',
       {
          'title':'Berper | Modify Spidered Scan',
          'form':form,
          'details':details
       },
       context_instance=RequestContext(request)
    )


def tscans(request):
    """Targeted Scans view
    
    """

    # If this is a POST, then validate the form and save the data
    if request.method == 'POST':
       
        # Check the form elements
        form = AddTScanForm(request.POST, request.FILES)

        if form.is_valid():
            # Obtain the cleaned data
            name = form.cleaned_data['name']
            site = form.cleaned_data['site']
            list = form.cleaned_data['list']
            time = form.cleaned_data['time']
            monday = form.cleaned_data['monday']
            tuesday = form.cleaned_data['tuesday']
            wednesday = form.cleaned_data['wednesday']
            thursday = form.cleaned_data['thursday']
            friday = form.cleaned_data['friday']
            saturday = form.cleaned_data['saturday']
            sunday = form.cleaned_data['sunday']
            method = form.cleaned_data['method']
            enabled = form.cleaned_data['enabled']
  
            # Add the scan name and obtain the name id
            Scan(name=name).save()
            name_id = Scan.objects.filter(name=name).values('id')[0]['id']

            # Add the site
            Site(name_id=name_id,site=site).save()

            # Whether enabled or not
            Scan_Enabled(name_id=name_id,enabled=enabled).save()

            # Add the schedule
            Schedule(
                name_id=name_id,
                time=time,
                monday=monday,
                tuesday=tuesday,
                wednesday=wednesday,
                thursday=thursday,
                friday=friday,
                saturday=saturday,
                sunday=sunday
                ).save()
            
            # Add the scan type
            Scan_Type(name_id=name_id,targeted=True).save()

            # Add the scan method
            Scan_Method(name_id=name_id,active=method).save()

            # Check each line in the list and add to the DB if not present
            # Form validation already ensures correct formatting so no need
            # for regex matching at this point, just insert them
            for line in list:
                # Remove any newlines/whitespace at the end
                line = line.rstrip()
                
                # Save the URL (handle duplicates) and obtain the ID
                try:
                    URL(url=line).save()
                except IntegrityError:
                    pass

                url_id = URL.objects.filter(url=line).values('id')[0]['id']
                
                # Save the URL with this scan and handle duplicates
                try:
                    TScan_List(name_id=name_id,url_id=url_id).save()
                except IntegrityError:
                    pass

            # Successful form submit so redirect 
            return HttpResponseRedirect('/adm/tscans')

        # Invalid form
        else:
            print 'Invalid form: AddTScanForm: %s' % form.errors

    
    # Its a GET or a failed POST
    else:
        # Create a blank add_tscan form
        form = AddTScanForm()

    # Obtain the targeted scans
    tscans = Scan.objects.filter(scan_type__targeted=True).values(
                                                            'id',
                                                            'name',
                                                            'site__site',
                                                            'scan_enabled__enabled',
                                                            'scan_method__active',
                                                            'schedule__time',
                                                            'schedule__monday',
                                                            'schedule__tuesday',
                                                            'schedule__wednesday',
                                                            'schedule__thursday',
                                                            'schedule__friday',
                                                            'schedule__saturday',
                                                            'schedule__sunday'
                                                            )

    # Print the page
    return render_to_response(
       'adm/tscans.html',
       {
          'title':'Berper | Targeted Scan Management',
          'form':form,
          'tscans':tscans
       },
       context_instance=RequestContext(request)
    )


def tscan_list(request):
    """Display a list of URLs for a targeted scan"""
        
    # Check the form elements
    form = URLListForm(request.GET)

    if form.is_valid():
        id = form.cleaned_data['id']
    
        # Obtain the urls
        urls = TScan_List.objects.filter(name_id=id).values('url_id__url')

        # Print the page
        return render_to_response(
           'adm/tscan_list.html',
           {
              'title':'Berper | Targeted Scan URL List',
              'urls':urls
           },
           context_instance=RequestContext(request)
        )

    # Invalid Form
    else:
        print 'Invalid form: URLListForm: %s' % form.errors

        # Send them back to the tscans list
        return HttpResponseRedirect('/adm/tscans')


def tscan_delete(request):
    """Delete a targeted scan"""
        
    # Check the form elements
    form = DeleteScanForm(request.GET)

    if form.is_valid():
        id = form.cleaned_data['id']
    
        # Obtain the urls
        Scan.objects.filter(id=id).delete()

    # Invalid Form
    else:
        print 'Invalid form: DeleteTScanForm: %s' % form.errors

    # Redirect to the targeted scan view
    return HttpResponseRedirect('/adm/tscans')


def sscan_delete(request):
    """Delete a spidered scan"""
        
    # Check the form elements
    form = DeleteScanForm(request.GET)

    if form.is_valid():
        id = form.cleaned_data['id']
    
        # Obtain the urls
        Scan.objects.filter(id=id).delete()

    # Invalid Form
    else:
        print 'Invalid form: DeleteScanForm: %s' % form.errors

    # Redirect to the spidered scan view
    return HttpResponseRedirect('/adm/sscans')


def tscan_modify(request):
    """Modify Targeted Scans view
    
    """

    # If this is a POST, then validate the form and save the data
    if request.method == 'POST':
       
        # Check the form elements
        form = ModifyTScanForm(request.POST, request.FILES)

        if form.is_valid():
            # Obtain the cleaned data - we are only allowing certain data
            # to be modified here
            id = form.cleaned_data['id']
            list = form.cleaned_data['list']
            time = form.cleaned_data['time']
            monday = form.cleaned_data['monday']
            tuesday = form.cleaned_data['tuesday']
            wednesday = form.cleaned_data['wednesday']
            thursday = form.cleaned_data['thursday']
            friday = form.cleaned_data['friday']
            saturday = form.cleaned_data['saturday']
            sunday = form.cleaned_data['sunday']
            method = form.cleaned_data['method']
            enabled = form.cleaned_data['enabled']
  
            # Modify the state of the scan
            Scan_Enabled.objects.filter(name_id=id).update(enabled=enabled)

            # Modify the schedule
            Schedule.objects.filter(name_id=id).update(
                time=time,
                monday=monday,
                tuesday=tuesday,
                wednesday=wednesday,
                thursday=thursday,
                friday=friday,
                saturday=saturday,
                sunday=sunday
                )
            
            # Modify the scan type
            Scan_Type.objects.filter(name_id=id).update(targeted=True)

            # Modify the scan method
            Scan_Method.objects.filter(name_id=id).update(active=method)

            # Check each line in the list (if submitted) and add to the DB if not present
            # Form validation already ensures correct formatting so no need
            # for regex matching at this point, just insert them
            if list:
                for line in list:
                    # Remove any newlines/whitespace at the end
                    line = line.rstrip()
                    
                    # Save the URL (handle duplicates) and obtain the ID
                    try:
                        URL(url=line).save()
                    except IntegrityError:
                        pass

                    url_id = URL.objects.filter(url=line).values('id')[0]['id']
                    
                    # Save the URL with this scan and handle duplicates
                    try:
                        TScan_List(name_id=id,url_id=url_id).save()
                    except IntegrityError:
                        pass

            # Successful form submit so redirect 
            return HttpResponseRedirect('/adm/tscans')

        # Invalid form
        else:
            print 'Invalid form: ModifyTScanForm: %s' % form.errors

    
    # Its a GET or a failed POST
    else:
        # Create a blank add_tscan form
        form = ModifyTScanForm()

    # We need the ID
    id = None
    if request.method == 'POST':
        if 'id' in request.POST:
            id = request.POST['id']
    else: 
        if 'id' in request.GET:
            id = request.GET['id']
    if id is None:
        return HttpResponseRedirect('/adm/tscans')


    # Obtain the targeted scans
    details = Scan.objects.filter(id=id).values(
                                                'id',
                                                'name',
                                                'site__site',
                                                'scan_enabled__enabled',
                                                'scan_method__active',
                                                'schedule__time',
                                                'schedule__monday',
                                                'schedule__tuesday',
                                                'schedule__wednesday',
                                                'schedule__thursday',
                                                'schedule__friday',
                                                'schedule__saturday',
                                                'schedule__sunday'
                                                )

    # Print the page
    return render_to_response(
       'adm/tscan_modify.html',
       {
          'title':'Berper | Modify Targeted Scan',
          'form':form,
          'details':details
       },
       context_instance=RequestContext(request)
    )

